package com.zhituanbox.extensions;

import com.zhituanbox.extensions.config.ExtensionsMessageSourceModeEnum;
import com.zhituanbox.extensions.config.ExtensionsMessageSourceProperties;
import com.zhituanbox.extensions.core.ExtensionsMessageSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
import org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.context.MessageSourceProperties;
import org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.AbstractResourceBasedMessageSource;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.core.Ordered;
import org.springframework.util.StringUtils;

import javax.validation.constraints.NotNull;
import java.time.Duration;

/**
 * 自动配置类
 * <ul>
 *     <li>自动配置MessageSource</li>
 *     <li>自动配置LocalValidatorFactoryBean</li>
 * </ul>
 *
 * @author LiuYang
 * @since 2020/9/21
 * @see MessageSourceAutoConfiguration
 * @see ValidationAutoConfiguration
 * @version 1.0.0
 */
@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@EnableConfigurationProperties
@ConditionalOnProperty(prefix = "spring.messages.extensions", value = "enable", havingValue = "true", matchIfMissing = true)
@AutoConfigureBefore(MessageSourceAutoConfiguration.class)
public class ExtensionsMessageSourceAutoConfiguration {

    private final Logger log = LoggerFactory.getLogger(ExtensionsMessageSourceAutoConfiguration.class);

    /**
     * <p>Constructor for ExtensionsMessageSourceAutoConfiguration.</p>
     */
    public ExtensionsMessageSourceAutoConfiguration() {
        log.info("ExtensionsMessageSource loading");
    }

    /**
     * <p>messageSourceProperties.</p>
     *
     * @return a {@link org.springframework.boot.autoconfigure.context.MessageSourceProperties} object.
     */
    @Bean
    @ConfigurationProperties(prefix = "spring.messages")
    public MessageSourceProperties messageSourceProperties() {
        return new MessageSourceProperties();
    }

    /**
     * <p>extensionsMessageSourceProperties.</p>
     *
     * @return a {@link com.zhituanbox.extensions.config.ExtensionsMessageSourceProperties} object.
     */
    @Bean
    @ConfigurationProperties(prefix = "spring.messages.extensions")
    public ExtensionsMessageSourceProperties extensionsMessageSourceProperties() {
        return new ExtensionsMessageSourceProperties();
    }

    /**
     * 构建MessageSource Bean
     * <p>根据配置返回组合不同的MessageSource实例</p>
     *
     * @param properties spring配置参数
     * @param extensionsProperties 扩展配置参数
     * @return ResourceBundleMessageSource|ExtensionsMessageSource
     */
    @Bean
    @ConditionalOnMissingBean(name = AbstractApplicationContext.MESSAGE_SOURCE_BEAN_NAME, search = SearchStrategy.CURRENT)
    public MessageSource messageSource(MessageSourceProperties properties, ExtensionsMessageSourceProperties extensionsProperties) {
        ResourceBundleMessageSource resourceBundleMessageSource = createResourceBundleMessageSource(properties);
        ExtensionsMessageSource extensionsMessageSource = createExtensionsMessageSource(properties, extensionsProperties);

        ExtensionsMessageSourceModeEnum mode = extensionsProperties.getMode();
        log.info("MessageSource load mode is {}({})", mode, mode.getDesc());
        switch (mode) {
            case ONLY_EXTENSIONS:
                return createExtensionsMessageSource(properties, extensionsProperties);
            case DEFAULT_THEN_EXTENSIONS:
                resourceBundleMessageSource.setParentMessageSource(extensionsMessageSource);
                return resourceBundleMessageSource;
            case EXTENSIONS_THEN_DEFAULT:
                extensionsMessageSource.setParentMessageSource(resourceBundleMessageSource);
                return extensionsMessageSource;
            default:
                return resourceBundleMessageSource;
        }
    }

    @NotNull
    private ExtensionsMessageSource createExtensionsMessageSource(MessageSourceProperties properties, ExtensionsMessageSourceProperties extensionsProperties) {
        ExtensionsMessageSource messageSource = new ExtensionsMessageSource();
        setAbstractResourceBasedMessageSource(messageSource, properties);
        return messageSource;
    }

    /**
     * 创建ResourceBundleMessageSource
     * @param properties 配置参数
     * @return ResourceBundleMessageSource
     */
    @NotNull
    private ResourceBundleMessageSource createResourceBundleMessageSource(MessageSourceProperties properties) {
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        setAbstractResourceBasedMessageSource(messageSource, properties);
        return messageSource;
    }

    /**
     * 配置AbstractResourceBasedMessageSource的基本数据
     * @param messageSource 消息源
     * @param properties 配置属性
     */
    private void setAbstractResourceBasedMessageSource(AbstractResourceBasedMessageSource messageSource, MessageSourceProperties properties) {
        if (StringUtils.hasText(properties.getBasename())) {
            messageSource.setBasenames(StringUtils
                    .commaDelimitedListToStringArray(StringUtils.trimAllWhitespace(properties.getBasename())));
        }
        if (properties.getEncoding() != null) {
            messageSource.setDefaultEncoding(properties.getEncoding().name());
        }
        messageSource.setFallbackToSystemLocale(properties.isFallbackToSystemLocale());
        Duration cacheDuration = properties.getCacheDuration();
        if (cacheDuration != null) {
            messageSource.setCacheMillis(cacheDuration.toMillis());
        }
        messageSource.setAlwaysUseMessageFormat(properties.isAlwaysUseMessageFormat());
        messageSource.setUseCodeAsDefaultMessage(properties.isUseCodeAsDefaultMessage());
    }
}
